printf 转换说明与修饰符速查
printf() 的格式串由转换说明控制,转换说明用错是 C 语言初学阶段最常见的输出异常来源:打印出乱码、数值位数不对、字段对不齐。本文整理转换说明、修饰符、标记三类成分的完整含义与书写顺序,并给出两个容易踩坑的细节(sizeof 的类型可移植性、float 参数的自动提升),可作为写格式串时的速查手册。
格式串的组成与书写顺序
一个完整的转换说明结构如下:
%[标记][最小字段宽度][.精度][长度修饰符]转换字符 除 % 和转换字符外,其余成分都可以省略。插入多个修饰符时,书写顺序应与上述结构一致,例如 %-+10.2f(左对齐、显式符号、宽度 10、精度 2、double),并非所有组合都有意义。
转换说明
转换说明(conversion specification)指定如何把数据转换成可显示的形式,打印指令要与待打印数据的类型匹配:
| 转换说明 | 输出 |
|---|---|
%a | 浮点数、十六进制数和 p 记数法(C99/C11) |
%A | 浮点数、十六进制数和 P 记数法(C99/C11) |
%c | 单个字符 |
%d | 有符号十进制整数 |
%e | 浮点数,e 记数法 |
%E | 浮点数,E 记数法 |
%f | 浮点数,十进制记数法 |
%F | 浮点数,十进制记数法(C99) |
%g | 根据值的不同,自动选择 %f 或 %e。%e 格式用于指数小于 -4 或大于等于精度时 |
%G | 根据值的不同,自动选择 %f 或 %E。%E 格式用于指数小于 -4 或大于等于精度时 |
%i | 有符号十进制整数(与 %d 相同) |
%o | 无符号八进制整数 |
%p | 指针 |
%s | 字符串 |
%u | 无符号十进制整数 |
%x | 无符号十六进制整数,用十六进制数字 af |
%X | 无符号十六进制整数,用十六进制数字 AF |
%% | 打印一个百分号 |
其中 %a/%A、%F 以及后文多个长度修饰符都是 C99 引入的,使用老编译器时需注意可用性。
修饰符
在 % 和转换字符之间插入修饰符,可以控制字段宽度与类型大小:
| 修饰符 | 含义 |
|---|---|
| 标记 | 5 种标记(-、+、空格、#、0),可以不使用或使用多个标记,示例:"%-10d" |
| 数字 | 最小字段宽度。如果该字段不能容纳待打印的数或字符串,系统会使用更宽的字段,示例:"%4d" |
.数字 | 精度。对于 %e、%E、%f 转换,表示小数点右边数字的位数;对于 %g、%G 转换,表示有效数字最大位数;对于 %s 转换,表示待打印字符的最大数量;对于整型转换,表示待打印数字的最小位数(如有必要,前面补零)。只使用 . 表示其后跟随一个 0,例如 %.f 与 %.0f 相同,示例:"%5.2f" |
h | 和整型转换说明一起使用,表示 short int 或 unsigned short int 类型,示例:"%hu" |
hh | 和整型转换说明一起使用,表示 signed char 或 unsigned char 类型,示例:"%hhu" |
j | 和整型转换说明一起使用,表示 intmax_t 或 uintmax_t 类型,示例:"%jd" |
l | 和整型转换说明一起使用,表示 long int 或 unsigned long int 类型,示例:"%ld" |
ll | 和整型转换说明一起使用,表示 long long int 或 unsigned long long int 类型(C99),示例:"%lld" |
L | 和浮点转换说明一起使用,表示 long double 类型,示例:"%Lf" |
t | 和整型转换说明一起使用,表示 ptrdiff_t 类型(与两个指针差值的类型对应,C99),示例:"%td" |
z | 和整型转换说明一起使用,表示 size_t 类型(C99),示例:"%zd" |
标记
标记控制对齐、符号与输出形式:
| 标记 | 含义 |
|---|---|
- | 待打印项左对齐,即从字段的左侧开始打印该项,示例:"%-20s" |
+ | 有符号值若为正,则在值前面显示加号;若为负,则在值前面显示减号,示例:"%+6.2f" |
| 空格 | 有符号值若为正,则在值前面显示前导空格(不显示任何符号);若为负,则在值前面显示减号。+ 标记覆盖一个空格,示例:"%6.2f" |
# | 把结果转换为另一种形式。如果是 %o 格式,则以 0 开始;如果是 %x 格式,则以 0x 开始;如果是 %X 格式,则以 0X 开始。对于所有的浮点格式,# 保证了即使后面没有任何数字,也打印一个小数点字符。对于 %g 和 %G 格式,# 防止结果后面的 0 被删除,示例:"%#o"、"%#8.0f" |
0 | 对于数值格式,用前导 0 代替空格填充字段宽度。对于整数格式,如果出现 - 标记或指定精度,则忽略该标记,示例:"%010d"、"%08.3f" |
两个容易踩坑的细节
sizeof 的类型可移植性。sizeof 运算符以字节为单位返回类型或值的大小,标准只规定了该值是无符号整数。在不同的实现中,它可以是 unsigned int、unsigned long 甚至是 unsigned long long。如果要用 printf() 显示 sizeof 表达式,根据不同系统,可能使用 %u、%lu 或 %llu,把程序移植到其他系统时还要进行修改。C 提供了可移植性更好的类型别名:stddef.h 头文件(在包含 stdio.h 时已包含其中)把 size_t 定义为 sizeof 返回的类型,printf() 应使用 %zd 转换说明打印 size_t 类型;对于两个指针的差值,C 标准提供了 ptrdiff_t 类型(定义在 stddef.h 中)和对应的 %td 转换说明(C99)。
float 参数的自动提升。对于浮点常量和 float 类型变量,printf() 都使用 %f、%e 等浮点转换说明。由于 C 语言中 float 类型的值在作为参数传递时会被自动提升为 double 类型(默认参数提升),printf() 的 float 类型参数实际上会先转换为 double 再传入,因此没有 float 专用的转换说明,也不需要。
要点
- 转换说明必须与待打印类型匹配,书写顺序为:
% 标记 宽度 .精度 长度修饰符 转换字符 - 打印 sizeof 的结果用
%zd,指针差值用%td,避免跨平台换%u/%lu - float 参数会自动提升为 double,直接用
%f即可 0标记在指定精度或与-标记同用时会被忽略
(内容依据《C Primer Plus》第 4 章整理,转换说明与修饰符行为以 C99/C11 标准为准。)